﻿#pragma once
#include "AsBasics.hpp"
#include "AsException.h"
#include <vector>
#include <map>

template <typename _TypeKey, typename _TypeValue>
class AsMap {
public:
	typedef std::map<_TypeKey, _TypeValue>					stdmap;
	typedef std::vector<_TypeKey>							stdkeys;
	typedef std::vector<_TypeValue>							stdvalues;
	typedef std::pair<_TypeKey, _TypeValue>					stdpair;
	typedef std::vector<std::pair<_TypeKey, _TypeValue>>	stdpairs;
	typedef typename stdmap::iterator iterator;
	typedef typename stdmap::const_iterator const_iterator;

public:
	AsMap();
	AsMap(const stdpairs& keyvalues);

private:
	stdmap map;

public:
	bool isEmpty() const;
	int size() const;

public:
	void insert(const _TypeKey& key, const _TypeValue& value);
	void clear();

public:
	bool contains(const _TypeKey& key) const;
	const _TypeValue& at(const _TypeKey& key) const;
	stdkeys keys() const;
	stdvalues values() const;
	stdpairs keyvalues() const;

public:
	_TypeValue& operator[](const _TypeKey& key);

public:
	iterator begin();
	iterator end();
	const_iterator cbegin() const;
	const_iterator cend() const;

private:
	struct ActionFind {
		ActionFind(const _TypeKey& key) : key(key), value(0), isFind(false) {}
		const _TypeKey& key;
		_TypeValue* value;
		bool isFind;
		bool operator()(std::pair<const _TypeKey, _TypeValue>& item) {
			if (item.first == key) {
				isFind = true;
				value = &item.second;
				return true;
			}
			return false;
		}
	};
	struct ActionConstFind {
		ActionConstFind(const _TypeKey& key) : key(key), value(0), isFind(false) {}
		const _TypeKey& key;
		const _TypeValue* value;
		bool isFind;
		bool operator()(const std::pair<const _TypeKey, _TypeValue>& item) {
			if (item.first == key) {
				isFind = true;
				value = &item.second;
				return true;
			}
			return false;
		}
	};
	struct ActionKeys {
		ActionKeys(const stdmap& map) { keys.reserve(map.size()); }
		stdkeys keys;
		bool operator()(const stdpair& item) {
			keys.emplace_back(item.first);
			return false;
		}
	};
	struct ActionValues {
		ActionValues(const stdmap& map) { values.reserve(map.size()); }
		stdvalues values;
		bool operator()(const stdpair& item) {
			values.emplace_back(item.second);
			return false;
		}
	};
	struct ActionKeysValues {
		ActionKeysValues(const stdmap& map) { pairs.reserve(map.size()); }
		stdpairs pairs;
		bool operator()(const stdpair& item) {
			pairs.emplace_back(item);
			return false;
		}
	};

};

template <typename _TypeKey, typename _TypeValue>
AsMap<_TypeKey, _TypeValue>::AsMap() {}

template <typename _TypeKey, typename _TypeValue>
AsMap<_TypeKey, _TypeValue>::AsMap(const stdpairs& keyvalues) {
	for (class stdpairs::const_iterator it = keyvalues.cbegin(); it != keyvalues.cend(); it++) {
		map.insert(it->first, it->second);
	}
}

template <typename _TypeKey, typename _TypeValue>
bool AsMap<_TypeKey, _TypeValue>::isEmpty() const {
	return map.empty();
}

template <typename _TypeKey, typename _TypeValue>
int AsMap<_TypeKey, _TypeValue>::size() const {
	return map.size();
}

template <typename _TypeKey, typename _TypeValue>
void AsMap<_TypeKey, _TypeValue>::insert(const _TypeKey& key, const _TypeValue& value) {
	map.insert(stdpair(key, value));
}

template <typename _TypeKey, typename _TypeValue>
void AsMap<_TypeKey, _TypeValue>::clear() {
	map.clear();
}

template <typename _TypeKey, typename _TypeValue>
bool AsMap<_TypeKey, _TypeValue>::contains(const _TypeKey& key) const {
	ActionConstFind action(key);
	As::cforeach(map, action);
	return action.isFind;
}

template <typename _TypeKey, typename _TypeValue>
const _TypeValue& AsMap<_TypeKey, _TypeValue>::at(const _TypeKey& key) const {
	ActionConstFind action(key);
	As::cforeach(map, action);
	if (!action.isFind) throw AsException(AsException::CodeMap_NotFindKey, "not find key");
	return *action.value;
}

template <typename _TypeKey, typename _TypeValue>
typename AsMap<_TypeKey, _TypeValue>::stdkeys AsMap<_TypeKey, _TypeValue>::keys() const {
	ActionKeys action(map);
	As::cforeach(map, action);
	return action.keys;
}

template <typename _TypeKey, typename _TypeValue>
typename AsMap<_TypeKey, _TypeValue>::stdvalues AsMap<_TypeKey, _TypeValue>::values() const {
	ActionValues action(map);
	As::cforeach(map, action);
	return action.values;
}

template <typename _TypeKey, typename _TypeValue>
typename AsMap<_TypeKey, _TypeValue>::stdpairs AsMap<_TypeKey, _TypeValue>::keyvalues() const {
	ActionKeysValues action(map);
	As::cforeach(map, action);
	return action.pairs;
}

template <typename _TypeKey, typename _TypeValue>
_TypeValue& AsMap<_TypeKey, _TypeValue>::operator[](const _TypeKey& key) {
	ActionFind action(key);
	As::_foreach(map, action);
	if (!action.isFind) throw AsException(AsException::CodeMap_NotFindKey, "not find key");
	return *action.value;
}

template <typename _TypeKey, typename _TypeValue>
typename AsMap<_TypeKey, _TypeValue>::iterator AsMap<_TypeKey, _TypeValue>::begin() {
	return map.begin();
}

template <typename _TypeKey, typename _TypeValue>
typename AsMap<_TypeKey, _TypeValue>::iterator AsMap<_TypeKey, _TypeValue>::end() {
	return map.end();
}

template <typename _TypeKey, typename _TypeValue>
typename AsMap<_TypeKey, _TypeValue>::const_iterator AsMap<_TypeKey, _TypeValue>::cbegin() const {
	return map.cbegin();
}

template <typename _TypeKey, typename _TypeValue>
typename AsMap<_TypeKey, _TypeValue>::const_iterator AsMap<_TypeKey, _TypeValue>::cend() const {
	return map.cend();
}